#ifndef __CAPAINDICES_ARBOLBMAS_ESTRATEGIABUSQUEDA_CPP_
#define __CAPAINDICES_ARBOLBMAS_ESTRATEGIABUSQUEDA_CPP_

/*****************************************************************************
*                     Organizacion de Datos - 75.06                          *
*                         Catedra Lic. Servetto                              *
*            Facultad de Ingenieria - Universidad de Buenos Aires            *
******************************************************************************/ 

#include "ArbolBMas.h"
#include <sstream>

using namespace std;
using namespace Comun;

namespace CapaIndices
{

	EstrategiaBusqueda::EstrategiaBusqueda(ArbolBMas *arbol, bool mutliple):Estrategia(arbol,mutliple){
	};

	EstrategiaBusqueda::~EstrategiaBusqueda(){		
	};

	Resultado EstrategiaBusqueda::BusquedaRecursiva(Clave *clave, Nodo *nodo, Nodo **padre, Registro *localizador, bool hallado){

		Nodo** siguiente = new Nodo*;		
		*siguiente = NULL;
		bool haySiguiente = false;

		if (Arbol->Metadata->TipoDato == Polivalente)
		{
			// Si se trata de una eliminacion multiple, verifica si las condiciones sobre la clave
			// requieren que se lea alguno de los nodos hermanos (menor o mayor).
			if (Multiple&& (padre != NULL))
				haySiguiente = VerificarRangoCondicion(clave,nodo,padre,localizador,siguiente);
			if (Arbol->Metadata->Tipo == Griego)
			{
				// Agrega todos los registros que cumplan con la condicion
				for (IteradorDeRegistros i = nodo->Registros.begin(); i!= nodo->Registros.end() ; i++)
				{
					// Verifica que se cumplan otras condiciones				
					if (clave->OtrasCondiciones->Analizar(*i))			
					{
						clave->Resultado->insert(clave->Resultado->end(),*i);
						clave->RegistrosAfectados++;
					}
				}

			}
			else
			{
				if (Multiple)
				{
					if (hallado)
					{
						BuscarRegistrosEnSecundario(nodo,clave,localizador,*Arbol->Metadata);

						for (IteradorDeRegistros n = (*padre)->Registros.begin();n!=(*padre)->Registros.end();n++)
						{
							if (obtenerIdHijoDerecho(*n) != nodo->GrupoId)
								if (clave->OtrasCondiciones->Analizar(*n))
								{
									Nodo* nod = new Nodo(Arbol->ArchivoDatos->LeerGrupo(obtenerIdHijoDerecho(*n)));
									BuscarRegistrosEnSecundario(nod,clave,localizador,*Arbol->Metadata);
								}
						}
					}
					else
					{
						for (IteradorDeRegistros n = (nodo)->Registros.begin();n!=(nodo)->Registros.end();n++)
						{
							if (obtenerIdHijoDerecho(*n) != nodo->GrupoId)
								if (clave->OtrasCondiciones->Analizar(*n))
								{
									Nodo* nod = new Nodo(Arbol->ArchivoDatos->LeerGrupo(obtenerIdHijoDerecho(*n)));
									BuscarRegistrosEnSecundario(nod,clave,localizador,*Arbol->Metadata);
								}
						}
					}
				}
				else
				{
					if (hallado)
						BuscarRegistrosEnSecundario(nodo,clave,localizador,*Arbol->Metadata);
				}

			}

			clave->FlushResultados(false);

			if (Multiple && haySiguiente)
				return BusquedaRecursiva(clave,*siguiente,padre,localizador,false);
			else
			{
				Resultado r;
				r.Codigo = Exito;
				return r;
			}		
		}else
		{
			if (hallado)
			{
				// Si se trata de una eliminacion multiple, verifica si las condiciones sobre la clave
				// requieren que se lea alguno de los nodos hermanos (menor o mayor).
				if (Multiple)
					haySiguiente = VerificarRangoCondicion(clave,nodo,padre,localizador,siguiente);

				if (Arbol->Metadata->Tipo == Griego)
				{
					// Agrega todos los registros que cumplan con la condicion
					for (IteradorDeRegistros i = nodo->Registros.begin(); i!= nodo->Registros.end() ; i++)
					{
						// Verifica que se cumplan otras condiciones				
						if (clave->OtrasCondiciones->Analizar(*i))			
						{
							clave->Resultado->insert(clave->Resultado->end(),*i);
							clave->RegistrosAfectados++;
						}
					}
				}
				else
					BuscarRegistrosEnSecundario(nodo,clave,localizador,*Arbol->Metadata);

				if (Multiple && haySiguiente)
					return BusquedaRecursiva(clave,*siguiente,padre,localizador,true);
				else
				{
					Resultado r;
					r.Codigo = Exito;
					return r;
				}		
			}
			else
			{
				Nodo* primerNodo;
				int hijo = obtenerIdHijoDerecho(*nodo->Registros.begin());
				Grupo g = Arbol->ArchivoDatos->LeerGrupo(hijo);
				primerNodo = new Nodo(g);
								
				// Si se trata de una eliminacion multiple, verifica si las condiciones sobre la clave
				// requieren que se lea alguno de los nodos hermanos (menor o mayor).
				if (Multiple)
					haySiguiente = VerificarRangoCondicion(clave,primerNodo,padre,localizador,siguiente);

				if (Arbol->Metadata->Tipo == Griego)
				{
					// Agrega todos los registros que cumplan con la condicion
					for (IteradorDeRegistros i = primerNodo->Registros.begin(); i!= primerNodo->Registros.end() ; i++)
					{
						// Verifica que se cumplan otras condiciones				
						if (clave->OtrasCondiciones->Analizar(*i))			
						{
							clave->Resultado->insert(clave->Resultado->end(),*i);
							clave->RegistrosAfectados++;
						}
					}

				}
				else
					BuscarRegistrosEnSecundario(nodo,clave,localizador,*Arbol->Metadata);

				if (Multiple && haySiguiente)
					return BusquedaRecursiva(clave,*siguiente,padre,localizador,true);
				else
				{
					Resultado r;
					r.Codigo = Exito;
					return r;
				}		


			}

		}
	}

	Resultado EstrategiaBusqueda::EjecutarEstrategia(CapaIndices::Clave *clave){		
		//	encontrar el nodo hoja que contiene el valor
		Resultado r = LocalizarClave(clave);
		// Nodo donde se encuentra el localizador
		Nodo* nodo = NULL;
		Nodo** padre = NULL;

		if (Camino.size() >= 2)
		{
			// Nodo donde se encuentra el localizador
			nodo = (*--Camino.end());
			padre = new Nodo*;
			*padre = (*--(--Camino.end()));
		}else
		{
			// Nodo donde se encuentra el localizador
			nodo = (*--Camino.end());

			if (nodo->Contador == 0)
			{
				Resultado r;
				r.Codigo = Exito;
				return r;
			}
		}

		// Obtiene el localizador
		Registro* localizador = NULL;

		if (clave->Resultado->size()>0)
			localizador = &(* clave->Resultado->begin());

		clave->Resultado->clear();

		bool hallado = ( r.Codigo == Exito);

		return BusquedaRecursiva(clave,nodo,padre,localizador,hallado);
	};

	void EstrategiaBusqueda::BuscarRegistrosEnSecundario(CapaIndices::Nodo *nodo, CapaIndices::Clave *clave, Comun::Registro *localizador, CapaIndices::MetadatosIndice metadata){

		// Obteniene el indice principal a partir de la subcapa de seleccion de indices
		IIndice* indicePrimario = clave->IndicePrimario;		

		// Por cada clave del indice primario, trae el registro desde el
		for (IteradorDeRegistros i = nodo->Registros.begin(); i != nodo->Registros.end(); i++ )
		{
			// Analizo si la clave primaria sigue cumpliendo la condicion
			if (clave->OtrasCondiciones->Analizar(*i))
			{	
				Clave* clavePrimaria = new Clave();

				// Copia las condiciones que debe cumplir el registro del indice primario
				clavePrimaria->OtrasCondiciones = new CondicionCompuestaAND();

				// Genera una clave primaria de busqueda a partir de los metadatos del indice
				for (IteradorDeMetadatosAtributos k = metadata.ClavePrimaria.begin(); k!=metadata.ClavePrimaria.end(); k++)
					for (IteradorDeAtributos j = (* i).Atributos.begin(); j!=(* i).Atributos.end(); j++)
						if ( (* k).Nombre == (* j).Nombre )
						{
							clavePrimaria->ValoresBusqueda.insert(clavePrimaria->ValoresBusqueda.end(),(* j).Valor);
							CondicionIgual* igual = new CondicionIgual();
							igual->ValorCondicion = (* j);
							clavePrimaria->OtrasCondiciones->Subcondiciones.push_back(igual);
						}

				indicePrimario->BuscarClave(clavePrimaria);

				int resultado = clavePrimaria->RegistrosAfectados;
				if (resultado == 1)
				{
					clave->Resultado->insert(clave->Resultado->end(),*clavePrimaria->Resultado->begin());
					clave->RegistrosAfectados++;
				}
			}
		}

		if (nodo->GrupoDesborde != -1)
		{
			Nodo* desborde = new Nodo(Arbol->ArchivoDatos->LeerGrupo(nodo->GrupoDesborde));
			BuscarRegistrosEnSecundario(desborde,clave,localizador,metadata);
		}
		
	};

}


#endif
